﻿using System.Reflection;

using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;


public class LuaTypeDefinition : LuaTypeReference
{
    private readonly LuaTypeContext _context;
    private IEnumerable<LuaFieldDefinition> _fields;
    private IEnumerable<LuaMethodDefinition> _methods;
    private IEnumerable<LuaEventDefinition> _events;
    private IEnumerable<LuaConstructorDefinition> _constructors;
    private LuaConstructorDefinition? _staticConstructors;
    private LuaTypeReference? _baseType;
    public LuaTypeContext Context => _context;
    public LuaTypeDefinition(LuaModuleDefinition module, TypeDefinition self) : base(module, self)
    {
        var staticConstructor = Resolve<TypeDefinition>().GetStaticConstructor();
        if (staticConstructor != null && staticConstructor.HasBody)
            _staticConstructors = new LuaConstructorDefinition(module, this, staticConstructor);
        var baseType = self.BaseType;
        if (baseType != null && baseType.FullName != "System.Object" && baseType.FullName != "System.ValueType")
            _baseType = module.GetTypeReference(baseType.FullName);
        _context = new LuaTypeContext(module, this);
    }

    public IEnumerable<LuaFieldDefinition> Fields
            => _fields ?? (_fields = Resolve<TypeDefinition>().Fields.Select(field => new LuaFieldDefinition(Module, this, field)));

    public IEnumerable<LuaMethodDefinition> Methods
        => _methods ?? (_methods = Resolve<TypeDefinition>().GetMethods()
        .Where(method => method.HasBody)
        .Select(method => new LuaMethodDefinition(Module, this, method)));

    public IEnumerable<LuaEventDefinition> Events
        => _events ?? (_events = Resolve<TypeDefinition>().Events.Select(e => new LuaEventDefinition(Module, this, e)));

    public IEnumerable<LuaConstructorDefinition> Constructors
        => _constructors ?? (_constructors = Resolve<TypeDefinition>().GetConstructors()
        .Where(method => method.HasBody)
        .Select(method => new LuaConstructorDefinition(Module, this, method)));

    public LuaConstructorDefinition StaticConstructor
        => _staticConstructors;

    public LuaTypeReference? BaseType
        => _baseType;

    public LuaFieldDefinition? GetField(string name)
        => Fields.FirstOrDefault(f => f.Self.Name == name || f.Name == name);

    public LuaFieldDefinition? GetField(FieldReference value)
    => Fields.FirstOrDefault(f => f.Self.FullName == value.FullName);

    public LuaMethodDefinition? GetMethod(string name)
        => Methods.FirstOrDefault(m => m.Self.Name == name || m.Name == name);

    public LuaMethodDefinition? GetMethod(MethodReference value)
        => Methods.FirstOrDefault(m => m.Self == value);

    public LuaEventDefinition? GetEvent(string name)
        => Events.FirstOrDefault(e => e.Self.Name == name || e.Name == name);

    public LuaEventDefinition? GetEvent(EventReference value)
        => Events.FirstOrDefault(e => e.Self == value);

    public LuaMethodReference GetMethodReference(LuaTypeReference type, MethodReference method)
        => _context.GetMethodReference(type, method);

    public LuaFieldReference GetFieldReference(LuaTypeReference type, FieldReference field)
        => _context.GetFieldReference(type, field);

    internal void WriteTo(ICodeWriter writer)
    {
        writer.PushColor(ConsoleColor.DarkGray);
        writer.Comment($"type:{Self.FullName}");
        writer.PushColor(ConsoleColor.Cyan);
        if (BaseType == null)
            writer.CodeLine($"local this = class('{FullName}')");
        else
            writer.CodeLine($"local this = class('{FullName}','{BaseType.FullName}')");

        foreach (var field in Fields)
            field.WriteTo(writer);

        foreach (var @event in Events)
            @event.WriteTo(writer);

        foreach (var method in Methods)
            method.WriteTo(writer);

        foreach (var constructor in Constructors)
            constructor.WriteTo(writer);

        writer.PopColor();
        writer.PopColor();
    }
}
